iT邦幫忙

2023 iThome 鐵人賽

DAY 29
0

#進階應用

進入進階應用前,要先跟各位分享目前環境,環境確認才方便後續變化使用。
接著可以先去 DEMO 確認資料格式,因為每張圖所需要的資料格式不一定相同,建議可以先去 DEMO 確認,這樣從後端取得資料後就能直接轉換。

  1. 撈取資料:
    引入 axios 後,先根據後端給的 api 網址撈取資料
  2. 分析資料格式:
    像上集介紹的柱狀圖,x 軸需要的是每個項目名稱的陣列, y 軸則是每個項目值的陣列;後端可以依照範例回傳然後前端直接渲染畫面,可以少跑回圈省效能,只是不符合後端格式規範害後端被罵,所以建議還是由前端轉換/images/emoticon/emoticon47.gif
// pie chart 需要的資料格式
const data = {
  categories: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子'],
  values: [5, 20, 36, 10, 10, 20]
}
// api 可能回傳的格式
const data = [
  {
    name: '衬衫',
    qty: 5,
  },
  {
    name: '羊毛衫',
    qty: 20,
  },
  ...
];

- 起手式

環境:Vue3 <script setup> + js + vite
套件:Apache ECharts 5.4 按需引入
重點:一定要記得設定寬高避免 resize 失敗

上集有提到如果是用按需引入,可以把需求元件掛載在 main.js 需要使用的地方再引入即可。
網路上比較多夥伴是把元件抽共用到一個新的 echart.jsexport default echarts; 引入 main.js,需要用到的元件用元件傳遞的方法傳送,其實這樣的作法在使用很多按需引入的套件時更好方便管理;這裡要示範,所以先統一寫在 main.js 當作我們的起手式。

可以參考這篇文章:在vue3.2中如何使用Echarts,按需导入(使用setup语法糖)
import { createApp } from 'vue';
import { createPinia } from 'pinia';

// 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。
import * as echarts from 'echarts/core';

// 引入柱状图图表,图表后缀都为 Chart
import { BarChart } from 'echarts/charts';

// 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
import {
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent
} from 'echarts/components';

// 标签自动布局、全局过渡动画等特性
import { LabelLayout, UniversalTransition } from 'echarts/features';

// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
import { CanvasRenderer } from 'echarts/renderers';

// 注册必须的组件
echarts.use([
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent,
  BarChart,
  LabelLayout,
  UniversalTransition,
  CanvasRenderer
]);

import App from './App.vue';
import router from './router';

const app = createApp(App);
const pinia = createPinia();

app.use(pinia);
app.use(router);

// Vue.js 元件傳遞
app.provide('echarts', echarts);
app.mount('#app');

- 擴大點擊事件範圍

現況:仔細看畫面,大家應該有發現當滑鼠靠近圖表其實有 tooltip 出現,接近該條線的數據游標才會轉成小手。
需求:今天滑鼠碰到星期幾點擊後,必須要顯示那天星期幾且 show 出當天花費明細!
滑鼠必須精準點到圖表的點才會出現小手游標
當然我們可以使用元件傳遞把星期帶入彈窗,但是...
問題:

  1. 在 canvas 中要怎麼準確把使用者點擊的星期找出
  2. 擴大點選範圍增加使用者體驗
    可以使用文件中提到的滑鼠觸發事件

events. 鼠标事件
https://ithelp.ithome.com.tw/upload/images/20230929/20158099kFb3MJ1HBf.jpg

重點在最下方的點擊觸發 myChart.getZr().on('click', function (params)

<template>
  <div ref="chart" class="chart_style"/>
</template>
  
<script setup>
import { onMounted, ref, inject } from 'vue';

// 在組件載入後初始化ECharts圖表
const echarts = inject('echarts');

// 模擬一些範例數據,這裡大部份可以取得後端資料後用陣列語法整理成以下需求格式
const data = [
  {
    name: '多拉a夢',
    type: 'line',
    stack: 'Total',
    data: [150, 230, 224, 218, 135, 147, 260]
  },
  ...
];

const chart = ref(null);
function render() {
  const myChart = echarts.init(chart.value);
  const option = {
    tooltip: {
      trigger: 'axis'
    },
    legend: {
      data: ['多拉a夢', '小夫', '胖虎']
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true
    },
    xAxis: {
      type: 'category',
      boundaryGap: false,
      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    yAxis: {
      type: 'value'
    },
    series: data
  };

  let week = ref('');
  myChart.getZr().on('click', function (params) {
    // 添加圖表的整個canvas區域的點擊事件
    console.log('params1::', params);

    // 獲取到鼠標點擊位置
    const pointInPixel = [params.offsetX, params.offsetY];
    console.log('pointInPixel::', pointInPixel);

    // 判斷點擊位置是否在顯示圖形區域,過濾canvas區域以外的位置
    if (myChart.containPixel('grid', pointInPixel)) {
      let xIndex = myChart.convertFromPixel({ seriesIndex: 0 }, [params.offsetX, params.offsetY])[0];
      console.log('11-點擊事件的dataIndex::', xIndex);

      // 運用找到的索引去對應option中的X軸位置找出名稱
      week.value = option.xAxis.data[xIndex];
      console.log('11-點擊事件的xAxisName::', week.value);
    }
  })

  myChart.setOption(option);
  const resize = () => myChart.resize();
  window.addEventListener('resize', () => {
    resize();
  })
}

onMounted(() => {
  render()
})
</script>
  

畫面展示
點擊後可以帶出日期,且不需要極精確點擊 symbol 才會觸發

- 運用屬性讓圖表說回憶

這個新增一個 markLine 屬性在同一張圖表

<template>
  <div ref="chart1" class="chart_style" />
</template>
  
<script setup>
import { onMounted, ref, inject } from 'vue';
import dayjs from 'dayjs';
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';

// 在组件加载后初始化ECharts图表
const echarts = inject('echarts');

// 模拟一些示例数据
dayjs.extend(isSameOrBefore);
const start_date = dayjs('2023-07-01');
const end_date = dayjs('2023-07-07');
const date_range = [];

for (let date = start_date; date.isSameOrBefore(end_date); date = date.add(1, 'day')) {
  date_range.push(date.format('YYYY-MM-DD'))
};
const data = {
  chart1: {
    data: [120, 132, 101, 134, 90, 230, 210],
    type: 'line'
  }
};

const chart1 = ref(null);
function render1() {
  const myChart = echarts.init(chart1.value);
  const option = {
    title: {
      text: '收入'
    },
    tooltip: {
      trigger: 'axis'
    },
    legend: {
      show: true
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true
    },
    xAxis: {
      type: 'category',
      boundaryGap: false,
      data: date_range
    },
    yAxis: {
      type: 'value'
    },
    series: {
      type: 'line',
      data: data.chart1.data,
      markLine: {
        silent: false,
        symbol: 'none',
        lineStyle: {
          color: '#333'
        },
        label: {
          show: true,
          fontSize: '11',
          backgroundColor: 'rgb(242,242,242)',
          borderColor: '#aaa',
          borderWidth: 1,
          borderRadius: 4,
          padding: [4, 10],
          lineHeight: 26,
          distance: 10,
          formatter: function (params) {
            console.log('params::', params)
            return params.name
          }
        },
        data: [
          {
            xAxis: '2023-07-04',
            name: '家庭聚會'
          }
        ]
      }
    }
  }

  myChart.setOption(option);
  const resize = () => myChart.resize();
  window.addEventListener('resize', () => {![/images/emoticon/emoticon20.gif](/images/emoticon/emoticon20.gif)
    resize();
  });
}

onMounted(() => {
  render1();
})
</script>
  

畫面展示
https://ithelp.ithome.com.tw/upload/images/20230929/201580993uD4JGX4B5.jpg

可能出現的 bug,其實是一定會出現的/images/emoticon/emoticon02.gif

github issue:Markline文字与Y轴文字重叠,有多条Markline时,线条与文字也会重叠,导致看不清楚 #5972
可能問題:如果文字太長,或是連續日期都需要標註標籤,導致標籤重疊,類似...2023-10-07 連假第一天;2023-10-08 連假第二天;2023-10-09 已經補班補課,放假一天;2023-10-10 國慶煙火在台中,歡迎攜伴一起欣賞美麗的煙火
處理方法:

  1. ...
    https://ithelp.ithome.com.tw/upload/images/20230929/2015809991Ox4cf9nh.jpg
  2. 啟動 hover:在 markLine 中的 emphasis
  3. 內容包裝到原生地 tooltip:在 tooltip 中用 formatter 客製化

- 多圖表聯動

這個目前我在基金網頁還沒看到,但是確實如果在相同 x 軸的屬性、範圍時候,不同張圖也可以做成聯動。
function render1() {...}function render2() {...} setOption 後做每個實例的 group id (可以任意命名,但切記:這是聯動的共用名字) 這樣就可以把不同圖表一起聯動。

<template>
  <div ref="chart1" class="chart_style" />
  <div ref="chart2" class="chart_style" />
</template>
  
<script setup>
import { onMounted, ref, inject } from 'vue';
import dayjs from 'dayjs';
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';

// 在组件加载后初始化ECharts图表
const echarts = inject('echarts');

// 模拟一些示例数据
dayjs.extend(isSameOrBefore);
const start_date = dayjs('2023-07-01');
const end_date = dayjs('2023-07-07');
const date_range = [];

for (let date = start_date; date.isSameOrBefore(end_date); date = date.add(1, 'day')) {
  date_range.push(date.format('YYYY-MM-DD'));
};
const data = {
  chart1: {
    data: [120, 132, 101, 134, 90, 230, 210],
    type: 'line'
  },
  chart2: {
    data: [220, 182, 191, 234, 290, 330, 310],
    type: 'line'
  }
}

const chart1 = ref(null);
const chart2 = ref(null);
function render1() {
  const myChart = echarts.init(chart1.value)
  const option = {
    title: {
      text: '收入'
    },
    ...
  }

  myChart.setOption(option);
  
  // 聯動的關鍵:做每個實例的 group id
  myChart.group = 'together';
  echarts.connect('together');
  const resize = () => myChart.resize();
  window.addEventListener('resize', () => {
    resize();
  })
}

function render2() {
  const myChart = echarts.init(chart2.value);
  const option = {
    title: {
      text: '支出'
    },
    ...
  }

  myChart.setOption(option);
  
  // 聯動的關鍵:做每個實例的 group id
  myChart.group = 'together';
  echarts.connect('together');
  const resize = () => myChart.resize();
  window.addEventListener('resize', () => {
    resize();
  });
}

onMounted(() => {
  render1();
  render2();
})
</script>
  

畫面展示
不同圖表的 tooltip 聯動


#小結

其實還有好多想要分享,就像基富通、商周的一些圖表,其實 ECharts.js 也可以做到,只是篇幅很像太多了,所以如果有需求可以在依照提示試試看,或是留言討論
https://ithelp.ithome.com.tw/upload/images/20230929/20158099sP51aFVneG.jpg

(圖表來源:基富通 ;方向:轉向柱狀圖)

https://ithelp.ithome.com.tw/upload/images/20230929/20158099xNuMa3Nm84.png

(圖表來源:商周百大顧問團 ;方向:markArea 屬性 )

上一篇
可視化圖表 - ECharts.js (中)
下一篇
旅程最終回 - vite 部署到 GitHub
系列文
Vue 元素美麗的轉變:前端小萌新勇闖套件的魔法陣30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言